﻿using System;
using System.Linq;
using System.Data.SQLite;
using System.IO;
using System.Data;
using System.Text;
using Log;

namespace RegLog
{
    /// <summary>
    /// Classe para registro de log em base de dados SQLite.
    /// </summary>
    public class LogSQLite : ILog
    {
        /// <summary>
        /// Define o banco de dados SQLite.
        /// </summary>
        public string DB { get; private set; }
        /// <summary>
        /// Define o caminho completo para acesso ao banco de dados SQLite.
        /// </summary>
        public string Caminho { get; private set; }
        /// <summary>
        /// Define o objeto de conexão com o banco de dados SQLite.
        /// </summary>
        public static SQLiteConnection Conexao { get; private set; }

        /// <summary>
        /// Inicializa os parâmetros necessários para registro de log em base de dados SQLite.
        /// </summary>
        /// <param name="caminho">Caminho completo para acesso ao arquivo de dados.</param>
        /// <param name="db">Nome da base de dados a utilizar.</param>        
        /// <param name="substituir">Define se a base de dados será ou não recriada.</param>
        public LogSQLite(string caminho, string db, bool substituir)
        {
            if (!Directory.Exists(caminho))
                Directory.CreateDirectory(caminho);
            DB = db.LastIndexOf(".sdb") == db.Length - 4 ? db : db + ".sdb";
            Caminho = System.IO.Path.Combine(caminho, DB);            
            try
            {
                if (!File.Exists(Caminho) || substituir) CriarBanco(caminho);
                else Conectar();
                RecriarTabelaLog();
            }
            catch (Exception ex)
            {
                throw ex;
            }            
        }

        private void Conectar()
        {
            if (Conexao == null) Conexao = new SQLiteConnection(@"Data Source=" + Caminho + ";Pooling=true;FailIfMissing=false");
            if (Conexao.State == ConnectionState.Closed) Conexao.Open();
        }

        private void CriarBanco(string caminho)
        {
            try
            {
                var sql = new StringBuilder();
                sql.AppendFormat("Data Source={0};", Caminho);
                sql.Append("Version=3;");
                sql.Append("New=True;");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private void Desconectar()
        {
            if (Conexao.State != ConnectionState.Closed)
                Conexao.Close();
        }

        private void RecriarTabelaLog()
        {
            var sql = new StringBuilder();
            sql.AppendLine("CREATE TABLE IF NOT EXISTS RegistroLog ([dataHora] DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, ");
            sql.AppendLine("[tipoLog] VARCHAR(20)  NOT NULL, [descricao] VARCHAR(200)  NOT NULL, [excecaoMensagem] VARCHAR(500)  NULL, ");
            sql.AppendLine("[excecaoSource] VARCHAR(500)  NULL, [excecaoStackTrace] VARCHAR(500)  NULL, [excecaoTargetSite] VARCHAR(500)  NULL)");
            if (Conexao == null || Conexao.State == ConnectionState.Closed) Conectar();
            var cmd = new SQLiteCommand { CommandText = sql.ToString(), Connection = Conexao };
            int ret = cmd.ExecuteNonQuery();
            Conexao.Close();
        }

        /// <summary>
        /// Registra o evento de log na base de dados.
        /// </summary>
        /// <param name="sender">Objeto disparador do evento.</param>
        /// <param name="e">Exceção repassada pelo objeto disparador do evento.</param>
        /// <param name="ret">Código de retorno para a aplicação que solicitou o registro.</param>
        public void Log(object sender, EventosLogEventArgs e, out int ret)
        {
            if (!string.IsNullOrWhiteSpace(Caminho))
            {
                if (Conexao == null || Conexao.State != ConnectionState.Open) Conectar();                         
                using (SQLiteTransaction tr = Conexao.BeginTransaction())
                {
                    try
                    {                        
                        var cmd = Conexao.CreateCommand();
                        cmd.Transaction = tr;
                        var tabela = "RegistroLog";
                        var sql = new StringBuilder();
                        sql.AppendFormat("INSERT INTO [{0}] ([dataHora], [tipoLog], [descricao], ", tabela);
                        sql.AppendLine("[excecaoMensagem], [excecaoSource], [excecaoStackTrace], [excecaoTargetSite]) ");
                        sql.AppendFormat("VALUES ('{0}','{1}', '{2}', '{3}', '{4}', '{5}', '{6}')", 
                            e.DataHora.ToString("u"), 
                            e.TipoLogString, 
                            e.Descricao, 
                            e.Exception.Message, 
                            e.Exception.Source, 
                            e.Exception.StackTrace, 
                            e.Exception.TargetSite);
                        cmd.CommandText = sql.ToString();
                        ret = cmd.ExecuteNonQuery();
                        tr.Commit();
                    }
                    catch (Exception)
                    {
                        ret = -1;
                        tr.Rollback();
                    }
                }
                Conexao.Close();
            }
            else
            {
                ret = -1;
                throw new Exception("A base de dados não foi encontrada.");
            }
        }
    }
}
